using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace Savage.DotNetLibrary.ProtocolLibrary
{
    public class EncryptionWrapper
    {
        public static byte[] GenerateHash(HashAlgorithm algorithm, string clearText)
        {
            return GenerateHash(algorithm, System.Text.Encoding.ASCII.GetBytes(clearText));
        }

        public static byte[] GenerateHash(HashAlgorithm algorithm, byte[] clearBytes)
        {
            return algorithm.ComputeHash(clearBytes);
        }
        
        public static byte[] Encrypt(byte[] clearData, string password, byte[] salt)
        { 
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);
            byte[] key = pdb.GetBytes(32);
            byte[] iv = pdb.GetBytes(16);

            return Encrypt(clearData, key, iv);
        }

        public static byte[] Encrypt(byte[] clearData, byte[] key, byte[] iv)
        {
            MemoryStream ms = new MemoryStream();
            Rijndael alg = Rijndael.Create();

            alg.Key = key;
            alg.IV = iv;

            CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);

            cs.Write(clearData, 0, clearData.Length);
            cs.Close();
            byte[] encryptedData = ms.ToArray();
            return encryptedData;
        }

        public static void Encrypt(string fileIn, string fileOut, string password, byte[] salt)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);
            byte[] key = pdb.GetBytes(32);
            byte[] iv = pdb.GetBytes(16);

            Encrypt(fileIn, fileOut, key, iv);
        }

        // Encrypt a file into another file using a password       
        public static void Encrypt(string fileIn, string fileOut, byte[] key, byte[] iv)       
        {             
            FileStream fsIn = new FileStream(fileIn, FileMode.Open, FileAccess.Read);             
            FileStream fsOut = new FileStream(fileOut, FileMode.OpenOrCreate, FileAccess.Write);               
            
            Rijndael alg = Rijndael.Create();               
            alg.Key = key;             
            alg.IV = iv;               
            
            CryptoStream cs = new CryptoStream(fsOut, alg.CreateEncryptor(), CryptoStreamMode.Write);               
            
            int bufferLen = 4096;             
            byte[] buffer = new byte[bufferLen];             
            int bytesRead;                         
            do 
            {                   
                bytesRead = fsIn.Read(buffer, 0, bufferLen);                     
                cs.Write(buffer, 0, bytesRead);                         
            } while(bytesRead != 0);               
            cs.Close(); 
            fsIn.Close();           
        }

        public static byte[] Decrypt(byte[] cipherData, string password, byte[] salt)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);
            byte[] key = pdb.GetBytes(32);
            byte[] iv = pdb.GetBytes(16);

            return Decrypt(cipherData, key, iv);
        }

        // Decrypt a byte array into a byte array using a key and an IV       
        public static byte[] Decrypt(byte[] cipherData, byte[] Key, byte[] IV)       
        {
            Rijndael alg = Rijndael.Create();

            alg.Key = Key;
            alg.IV = IV;

            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherData, 0, cipherData.Length);
                    cs.Close();
                }
                
                return ms.ToArray();
            }       
        }

        public static void Decrypt(string fileIn, string fileOut, string password, byte[] salt)
        {
            PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt);
            byte[] key = pdb.GetBytes(32);
            byte[] iv = pdb.GetBytes(16);

            Decrypt(fileIn, fileOut, key, iv);
        }

        // Decrypt a file into another file using a password       
        public static void Decrypt(string fileIn, string fileOut, byte[] key, byte[] iv)       
        {             
            //open the file streams             
            FileStream fsIn = new FileStream(fileIn, FileMode.Open, FileAccess.Read);             
            FileStream fsOut = new FileStream(fileOut, FileMode.OpenOrCreate, FileAccess.Write);               
            
            Rijndael alg = Rijndael.Create();
            alg.Key = key;
            alg.IV = iv;

            CryptoStream cs = new CryptoStream(fsOut, alg.CreateDecryptor(), CryptoStreamMode.Write);               
            
            int bufferLen = 4096;             
            byte[] buffer = new byte[bufferLen];             
            int bytesRead;                         
            do 
            {                   
                // read a chunk of data from the input file                   
                bytesRead = fsIn.Read(buffer, 0, bufferLen);                     
                // Decrypt it                   
                cs.Write(buffer, 0, bytesRead);                         
            } while(bytesRead != 0);               
            
            // close everything             
            cs.Close(); 
            // this will also close the unrelying fsOut stream             
            fsIn.Close();           
        } 

    }
}
